package com.gitee.yongzhuzl.commonutil.util.redis.boot;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * description: Redis模板工具类
 * @author 朱正磊
 * @date 2019-10-18 09:47:38
 */
@Component
public final class RedisTemplateUtil {

    private static final Logger log = LoggerFactory.getLogger(RedisTemplateUtil.class);

    @Resource
    private RedisTemplate<String, Object> zdyRedisTemplate;

    // =============================common============================

    /**
     * description: 设置缓存过期时间(设置相对过期时间)
     * @author 朱正磊
     * @date 2019-10-18 09:48:49
     * @param key 键
     * @param time 相对过期时间(秒)
     * @return 设置成功返回true，否则返回false
     */
    public Boolean expire(String key, Long time) {
        Assert.notNull(key, "键不能为null！");
        Assert.state(time > 0, "过期时间(秒)必须大于0！");
        try {
            return zdyRedisTemplate.expire(key, time, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("设置缓存过期时间异常信息：", e);
        }
        return false;
    }

    /**
     * description: 设置缓存过期时间(设置绝对过期时间点)
     * @author 朱正磊
     * @date 2025-11-25 14:40:03
     * @param key 键
     * @param date 绝对过期时间点
     * @return 设置成功返回true，否则返回false
     */
    public Boolean expireAt(String key, Date date) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(date, "过期时间点不能为null！");
        try {
            return zdyRedisTemplate.expireAt(key, date);
        } catch (Exception e) {
            log.error("设置缓存过期时间点异常信息：", e);
        }
        return false;
    }

    /**
     * description: 根据key获取过期时间
     * @author 朱正磊
     * @date 2019-10-18 09:58:22
     * @param key 键，不能为null
     * @return 返回时间(秒)，返回0代表永不过期
     */
    public Long getExpire(String key) {
        Assert.notNull(key, "键不能为null！");
        return zdyRedisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * description: 判断key是否存在
     * @author 朱正磊
     * @date 2019-10-18 09:59:49
     * @param key 键
     * @return 如果存在返回true，否则返回false
     */
    public Boolean exists(String key) {
        Assert.notNull(key, "键不能为null！");
        try {
            return zdyRedisTemplate.hasKey(key);
        } catch (Exception e) {
            log.error("判断key是否存在异常信息：", e);
        }
        return false;
    }

    /**
     * description: 删除缓存
     * @author 朱正磊
     * @date 2019-10-18 10:03:03
     * @param key 一个或多个
     */
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                zdyRedisTemplate.delete(key[0]);
            } else {
                zdyRedisTemplate.delete(Arrays.asList(key));
            }
        }
    }

    // ============================String=============================

    /**
     * description: 获取指定类型的缓存
     * @author 朱正磊
     * @date 2019-10-18 10:04:16
     * @param key 键，不能为null
     * @param returnType 返回值类型的Class对象
     * @return 返回值，不存在时返回null
     */
    public <R> R get(String key, Class<R> returnType) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(returnType, "返回值类型Class不能为null！");

        Object value = zdyRedisTemplate.opsForValue().get(key);
        if (value == null) {
            return null;
        }

        if (returnType.isInstance(value)) {
            return returnType.cast(value);
        }

        String valueClass = value.getClass().getName();
        String returnTypeClass = returnType.getName();
        String msg = String.format("缓存值类型[%s]与目标类型[%s]不匹配", valueClass, returnTypeClass);
        throw new IllegalArgumentException(msg);
    }

    /**
     * description: 添加String类型的缓存
     * @author 朱正磊
     * @date 2019-10-18 10:19:05
     * @param key 键
     * @param value 值
     * @return 添加成功返回true，否则返回false
     */
    public <T> Boolean set(String key, T value) {
        Assert.notNull(key, "键不能为null！");
        try {
            zdyRedisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            log.error("添加String类型的缓存异常信息：", e);
        }
        return false;
    }

    /**
     * description: 添加String类型的缓存并设置过期时间
     * @author 朱正磊
     * @date 2019-10-18 10:22:06
     * @param key 键
     * @param value 值
     * @param time 时间(秒)，time要大于0
     * @return 设置成功返回true，否则返回失败
     */
    public <T> Boolean set(String key, T value, Long time) {
        Assert.notNull(key, "键不能为null！");
        Assert.state(time > 0, "过期时间(秒)必须大于0！");
        try {
            zdyRedisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            log.error("添加String类型的缓存并设置过期时间异常信息：", e);
        }
        return false;
    }

    /**
     * description: 递增
     * @author 朱正磊
     * @date 2019-10-18 10:27:01
     * @param key 键
     * @param delta 要增加几(大于0)
     * @return 返回递增后的值
     */
    public Long incr(String key, Long delta) {
        Assert.notNull(key, "键不能为null！");
        Assert.state(delta > 0, "递增因子必须大于0！");
        return zdyRedisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * description: 递减
     * @author 朱正磊
     * @date 2019-10-18 10:36:29
     * @param key 键
     * @param delta 要减少几(大于0)
     * @return 返回递减后的值
     */
    public Long decr(String key, Long delta) {
        Assert.notNull(key, "键不能为null！");
        Assert.state(delta > 0, "递减因子必须大于0！");
        return zdyRedisTemplate.opsForValue().decrement(key, delta);
    }

    /**
     * 温馨提醒：该方法等同于redis的SETNX命令
     * description: 当键不存在时设置键的值，
     *              即在指定的key不存在时才会为key设置指定的值，否则不会为key执行设置操作即设置失败
     * @author 朱正磊
     * @date 2019-12-03 15:03:48
     * @param key 键
     * @param value 值
     * @return 设置成功返回true，设置失败返回false
     */
    public <T> Boolean setIfAbsent(String key, T value) {
        Assert.notNull(key, "键不能为null！");
        return zdyRedisTemplate.opsForValue().setIfAbsent(key, value);
    }

    /**
     * 温馨提醒：该方法等同于redis的SETNX命令
     * description: 当键不存在时设置键的值并设置过期时间，
     *              即在指定的key不存在时才会为key设置指定的值，否则不会为key执行设置操作即设置失败
     * @author 朱正磊
     * @date 2019-12-03 15:03:54
     * @param key 键
     * @param value 值
     * @param timeout 过期时间(秒)，timeout要大于0
     * @return 设置成功返回true，设置失败返回false
     */
    public <T> Boolean setIfAbsent(String key, T value, Long timeout) {
        Assert.notNull(key, "键不能为null！");
        Assert.state(timeout > 0, "过期时间(秒)必须大于0！");
        return zdyRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);
    }

    /**
     * 温馨提醒：该方法等同于redis的SETNX命令
     * description: 当键不存在时设置键的值并设置过期时间，
     *              即在指定的key不存在时才会为key设置指定的值，否则不会为key执行设置操作即设置失败
     * @author 朱正磊
     * @date 2025-04-11 09:43:53
     * @param key 键
     * @param value 值
     * @param timeout 过期时间，timeout要大于0
     * @param timeUnit 时间单位
     * @return 设置成功返回true，设置失败返回false
     * @param <T> 泛型
     */
    public <T> Boolean setIfAbsent(String key, T value, Long timeout, TimeUnit timeUnit) {
        Assert.notNull(key, "键不能为null！");
        Assert.state(timeout > 0, "过期时间必须大于0！");
        return zdyRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, timeUnit);
    }

    /**
     * 温馨提醒：该方法等同于redis的SETNX命令
     * description: 当键不存在时设置键的值并设置过期时间，
     *              即在指定的key不存在时才会为key设置指定的值，否则不会为key执行设置操作即设置失败
     * @author 朱正磊
     * @date 2025-04-11 09:44:03
     * @param key 键
     * @param value 值
     * @param timeout 表示键值对的过期时间，Duration 是 Java 8 引入的一个类，用于表示一段时间，比如几秒、几分钟等。
     * @return 设置成功返回true，设置失败返回false
     * @param <T> 泛型
     */
    public <T> Boolean setIfAbsent(String key, T value, Duration timeout) {
        Assert.notNull(key, "键不能为null！");
        return zdyRedisTemplate.opsForValue().setIfAbsent(key, value, timeout);
    }

    /**
     * 温馨提醒：该方法等同于redis的GETSET命令
     * description: 将给定key的值设为value，并返回 key的旧值(old value)，即先获取key之前的值，然后将key的值设置为value
     * @author 朱正磊
     * @date 2019-12-03 15:55:44
     * @param key 键(不能为null)
     * @param value 新值
     * @param returnType 旧值类型的Class对象
     * @return 返回key之前的值(旧值)，如果不存在则返回null
     */
    public <T, R> R getAndSet(String key, T value, Class<R> returnType) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(returnType, "返回值类型Class不能为null！");

        Object oldValue = zdyRedisTemplate.opsForValue().getAndSet(key, value);
        if (oldValue == null) {
            return null;
        }

        if (returnType.isInstance(oldValue)) {
            return returnType.cast(oldValue);
        }

        String oldValueClass = oldValue.getClass().getName();
        String returnTypeClass = returnType.getName();
        String msg = String.format("键[%s]的旧值类型[%s]与目标类型[%s]不匹配", key, oldValueClass, returnTypeClass);
        throw new IllegalArgumentException(msg);
    }

    // ================================Map=================================

    /**
     * description: 获取Hash类型的缓存
     * @author 朱正磊
     * @date 2019-10-18 10:53:58
     * @param key 键，不能为null
     * @param hashKey 项，不能为null
     * @param returnType 返回值类型的Class对象
     * @return 返回值，不存在时返回null
     */
    public <T, R> R hget(String key, T hashKey, Class<R> returnType) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(hashKey, "项不能为null！");
        Assert.notNull(returnType, "返回值类型Class不能为null！");

        Object value = zdyRedisTemplate.opsForHash().get(key, hashKey);
        if (value == null) {
            return null;
        }

        if (returnType.isInstance(value)) {
            return returnType.cast(value);
        }

        String valueClass = value.getClass().getName();
        String returnTypeClass = returnType.getName();
        String msg = String.format("Hash键[%s]的项[%s]的值类型[%s]与目标类型[%s]不匹配", key, hashKey, valueClass, returnTypeClass);
        throw new IllegalArgumentException(msg);
    }

    /**
     * description: 获取Hash类型对应的所有键值
     * @author 朱正磊
     * @date 2019-10-18 11:21:46
     * @param key 键，不能为null
     * @param keyType hashKey的类型Class对象
     * @param valueType hashValue的类型Class对象
     * @return 返回对应的多个键值对Map
     */
    public <T, R> Map<T, R> hmget(String key, Class<T> keyType, Class<R> valueType) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(keyType, "hashKey类型Class不能为null！");
        Assert.notNull(valueType, "hashValue类型Class不能为null！");

        Map<Object, Object> entries = zdyRedisTemplate.opsForHash().entries(key);
        Map<T, R> resultMap = new HashMap<>(entries.size());

        for (Map.Entry<Object, Object> entry : entries.entrySet()) {
            Object enteryKey = entry.getKey();
            if (!keyType.isInstance(enteryKey)) {
                throw new IllegalArgumentException(
                        String.format("Hash键[%s]中的项[%s]类型[%s]与目标key类型[%s]不匹配",
                                key, enteryKey, enteryKey.getClass().getName(), keyType.getName())
                );
            }

            Object value = entry.getValue();
            if (value != null && !valueType.isInstance(value)) {
                throw new IllegalArgumentException(
                        String.format("Hash键[%s]中的项[%s]的值类型[%s]与目标value类型[%s]不匹配",
                                key, enteryKey, value.getClass().getName(), valueType.getName())
                );
            }

            resultMap.put(keyType.cast(enteryKey), valueType.cast(value));
        }

        return resultMap;
    }

    /**
     * description: 设置Hash类型的值
     * @author 朱正磊
     * @date 2019-10-18 11:23:14
     * @param key 键
     * @param map 对应多个键值
     * @return 设置成功返回true，否则返回false
     */
    public <T, R> Boolean hmset(String key, Map<T, R> map) {
        try {
            zdyRedisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            log.error("设置Hash类型的值异常信息：", e);
        }
        return false;
    }

    /**
     * description: 设置Hash类型的值并设置过期时间
     * @author 朱正磊
     * @date 2019-10-18 11:26:03
     * @param key 键
     * @param map 对应多个键值
     * @param time 时间(秒)
     * @return 设置成功返回true，否则返回false
     */
    public <T, R> Boolean hmset(String key, Map<T, R> map, Long time) {
        try {
            zdyRedisTemplate.opsForHash().putAll(key, map);
            return expire(key, time);
        } catch (Exception e) {
            log.error("设置Hash类型的值并设置过期时间异常信息：", e);
        }
        return false;
    }

    /**
     * description: 向一张hash表中放入数据，如果key不存在将创建
     * @author 朱正磊
     * @date 2019-10-18 11:37:16
     * @param key 键
     * @param hashKey 项
     * @param value 值
     * @return 设置成功返回true，否则返回false
     */
    public <T, R> Boolean hset(String key, T hashKey, R value) {
        try {
            zdyRedisTemplate.opsForHash().put(key, hashKey, value);
            return true;
        } catch (Exception e) {
            log.error("向一张hash表中放入数据，如果key不存在将创建异常信息：", e);
        }
        return false;
    }

    /**
     * description: 向一张hash表中放入数据，如果key不存在将创建，并设置过期时间
     * @author 朱正磊
     * @date 2019-10-18 11:40:44
     * @param key 键
     * @param hashKey 项
     * @param value 值
     * @param time 时间(秒) 注意：如果已存在的hash表有时间，这里将会替换原有的时间
     * @return 设置成功返回true，否则返回false
     */
    public <T, R> Boolean hset(String key, T hashKey, R value, Long time) {
        try {
            zdyRedisTemplate.opsForHash().put(key, hashKey, value);
            return expire(key, time);
        } catch (Exception e) {
            log.error("向一张hash表中放入数据，如果key不存在将创建，并设置过期时间异常信息：", e);
        }
        return false;
    }

    /**
     * description: 根据field项删除Hash表中的值
     * @author 朱正磊
     * @date 2019-10-18 11:51:42
     * @param key 键，不能为null
     * @param hashKeys 项，可变参数，至少需要一个且不能为null
     */
    @SafeVarargs
    public final <T> void hdel(String key, T... hashKeys) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(hashKeys, "项数组不能为null！");
        Assert.isTrue(hashKeys.length > 0, "至少需要指定一个项(hashKey)！");

        zdyRedisTemplate.opsForHash().delete(key, (Object[]) hashKeys);
    }

    /**
     * description: 判断Hash表中是否有该项的值
     * @author 朱正磊
     * @date 2019-10-18 11:57:48
     * @param key 键，不能为null
     * @param hashKey 项，不能为null
     * @return 如果有返回true，否则返回false
     */
    public <T> Boolean hexists(String key, T hashKey) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(hashKey, "项不能为null！");
        return zdyRedisTemplate.opsForHash().hasKey(key, hashKey);
    }

    /**
     * description: hash递增，如果不存在就会创建一个并把递增后的值返回
     * @author 朱正磊
     * @date 2019-10-18 13:01:48
     * @param key 键
     * @param hashKey 项
     * @param by 递增值，即要增加几(大于0)
     * @return 返回递增后的值
     */
    public <T> Double hincr(String key, T hashKey, Double by) {
        Assert.state(by > 0, "递增值要大于0！");
        return zdyRedisTemplate.opsForHash().increment(key, hashKey, by);
    }

    /**
     * description: hash递减，如果不存在就会创建一个并把递减后的值返回
     * @author 朱正磊
     * @date 2019-10-18 13:05:40
     * @param key 键
     * @param hashKey 项
     * @param by 要减少几(大于0)
     * @return 返回递减后的值
     */
    public <T> Double hdecr(String key, T hashKey, Double by) {
        Assert.state(by > 0, "递减值要大于0！");
        return zdyRedisTemplate.opsForHash().increment(key, hashKey, -by);
    }

    // ============================set=============================

    /**
     * description: 根据key获取Set中的所有值
     * @author 朱正磊
     * @date 2019-10-18 13:13:45
     * @param key key 键，不能为null
     * @param elementType Set中元素的类型Class对象
     * @return 返回Set中key对应的所有值，若key不存在则返回空集合(非null)
     */
    public <T> Set<T> sget(String key, Class<T> elementType) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(elementType, "元素类型Class不能为null！");

        try {
            Set<Object> originalSet = zdyRedisTemplate.opsForSet().members(key);
            if (originalSet == null || originalSet.isEmpty()) {
                return Collections.emptySet();
            }

            Set<T> resultSet = new HashSet<>(originalSet.size());
            for (Object element : originalSet) {
                if (element == null) {
                    resultSet.add(null);
                } else if (elementType.isInstance(element)) {
                    resultSet.add(elementType.cast(element));
                } else {
                    throw new IllegalArgumentException(
                            String.format("Set键[%s]中的元素[%s]类型[%s]与目标类型[%s]不匹配",
                                    key, element, element.getClass().getName(), elementType.getName())
                    );
                }
            }
            return resultSet;
        }  catch (IllegalArgumentException e) {
            throw e;
        } catch (Exception e) {
            log.error("获取Set类型缓存异常，key: {}", key, e);
            return Collections.emptySet();
        }
    }

    /**
     * description: 判断value是否在key中存在
     * @author 朱正磊
     * @date 2019-10-18 13:15:57
     * @param key 键
     * @param value 值
     * @return 如果存在返回true，如果不存在返回false
     */
    public <T> Boolean sexists(String key, T value) {
        try {
            return zdyRedisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            log.error("判断value是否在key中存在异常信息：", e);
        }
        return false;
    }

    /**
     * description: 将数据放入set缓存
     * @author 朱正磊
     * @date 2019-10-18 13:19:33
     * @param key 键，不能为null
     * @param values 值，可变参数，至少需要一个且不能为null
     * @return 返回放入Set缓存里面的值的个数(新增的元素数量，已存在的元素不会被重复计数)
     */
    @SafeVarargs
    public final <T> Long sset(String key, T... values) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(values, "值数组不能为null！");
        Assert.isTrue(values.length > 0, "至少需要指定一个值！");

        try {
            return zdyRedisTemplate.opsForSet().add(key, (Object[]) values);
        } catch (Exception e) {
            log.error("向Set集合添加元素异常，key: {}", key, e);
            return 0L;
        }
    }

    /**
     * description: 将set数据放入缓存并设置过期时间
     * @author 朱正磊
     * @date 2019-10-18 13:22:06
     * @param key 键，不能为null
     * @param time 过期时间(秒)，必须大于0
     * @param values 值，可变参数，至少需要一个且不能为null
     * @return 返回放入Set缓存里面的新值个数(已存在的元素不计数)
     */
    @SafeVarargs
    public final <T> Long ssetAndTime(String key, Long time, T... values) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(time, "过期时间不能为null！");
        Assert.isTrue(time > 0, "过期时间必须大于0秒！");
        Assert.notNull(values, "值数组不能为null！");
        Assert.isTrue(values.length > 0, "至少需要指定一个值！");

        try {
            Long addedCount = zdyRedisTemplate.opsForSet().add(key, (Object[]) values);
            if (addedCount == null || addedCount <= 0) {
                return 0L;
            }
            Boolean expire = expire(key, time);
            if (expire) {
                return addedCount;
            }
        } catch (Exception e) {
            log.error("向Set集合添加元素并设置过期时间异常，key: {}", key, e);
        }
        return 0L;
    }

    /**
     * description: 获取set缓存的长度
     * @author 朱正磊
     * @date 2019-10-18 13:26:24
     * @param key 键
     * @return 返回key对应的Set的集合长度
     */
    public Long sgetSetSize(String key) {
        try {
            return zdyRedisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            log.error("获取set缓存的长度异常信息：", e);
        }
        return 0L;
    }

    /**
     * description: 从Set缓存里面移除key中的值
     * @author 朱正磊
     * @date 2019-10-18 13:28:41
     * @param key 键，不能为null
     * @param values 要移除的值，可变参数，至少需要一个且不能为null
     * @return 返回实际移除的值的个数(不存在的元素不会被计数)
     */
    @SafeVarargs
    public final <T> Long sremove(String key, T... values) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(values, "要移除的值数组不能为null！");
        Assert.isTrue(values.length > 0, "至少需要指定一个要移除的值！");

        try {
            return zdyRedisTemplate.opsForSet().remove(key, (Object[]) values);
        } catch (Exception e) {
            log.error("从Set集合移除元素异常，key: {}", key, e);
            return 0L;
        }
    }

    // ===============================list=================================

    /**
     * description: 获取list缓存的所有内容(等价于lget(key, 0L, -1L))
     * @author 朱正磊
     * @date 2019-10-18 13:31:10
     * @param key 键，不能为null
     * @param elementType 列表元素的类型Class对象
     * @return 返回list缓存的所有内容，若不存在则返回空列表(非null)
     */
    public <R> List<R> lget(String key, Class<R> elementType) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(elementType, "元素类型Class不能为null！");

        return lget(key, 0L, -1L, elementType);
    }

    /**
     * description: 获取list缓存的内容(索引0到-1代表获取所有值)
     * @author 朱正磊
     * @date 2019-10-18 13:31:10
     * @param key 键，不能为null
     * @param start 开始索引，不能为null
     * @param end 结束索引，不能为null
     * @param elementType 列表元素的类型Class对象
     * @return 返回list缓存的内容，若不存在则返回空列表(非null)
     */
    public <R> List<R> lget(String key, Long start, Long end, Class<R> elementType) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(start, "开始索引不能为null！");
        Assert.notNull(end, "结束索引不能为null！");
        Assert.notNull(elementType, "元素类型Class不能为null！");

        try {
            List<Object> originalList = zdyRedisTemplate.opsForList().range(key, start, end);
            if (originalList == null) {
                return Collections.emptyList();
            }

            List<R> resultList = new ArrayList<>(originalList.size());
            for (Object element : originalList) {
                if (element == null) {
                    resultList.add(null);
                } else if (elementType.isInstance(element)) {
                    resultList.add(elementType.cast(element));
                } else {
                    throw new IllegalArgumentException(
                            String.format("List键[%s]中的元素[%s]类型[%s]与目标类型[%s]不匹配",
                                    key, element, element.getClass().getName(), elementType.getName())
                    );
                }
            }
            return resultList;
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (Exception e) {
            log.error("获取List类型缓存异常，key: {}, start: {}, end: {}", key, start, end, e);
            return Collections.emptyList();
        }
    }

    /**
     * description: 获取list缓存的长度
     * @author 朱正磊
     * @date 2019-10-18 13:33:35
     * @param key 键
     * @return 返回list缓存的长度
     */
    public Long lgetListSize(String key) {
        try {
            return zdyRedisTemplate.opsForList().size(key);
        } catch (Exception e) {
            log.error("获取list缓存的长度异常信息：", e);
        }
        return 0L;
    }

    /**
     * description: 通过索引获取list中的值
     * @author 朱正磊
     * @date 2019-10-18 13:35:41
     * @param key 键，不能为null
     * @param index 索引，当index>=0时，0表示第一个元素；当index<0时，-1表示倒数第一个元素
     * @param elementType 元素的类型Class对象
     * @return 返回根据索引从list缓存中获取的值，不存在时返回null
     */
    public <R> R lgetIndex(String key, Long index, Class<R> elementType) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(index, "索引不能为null！");
        Assert.notNull(elementType, "元素类型Class不能为null！");

        try {
            Object element = zdyRedisTemplate.opsForList().index(key, index);
            if (element == null) {
                return null;
            }

            if (elementType.isInstance(element)) {
                return elementType.cast(element);
            }

            throw new IllegalArgumentException(
                    String.format("List键[%s]中索引[%d]的元素[%s]类型[%s]与目标类型[%s]不匹配",
                            key, index, element, element.getClass().getName(), elementType.getName())
            );
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (Exception e) {
            log.error("通过索引获取List元素异常，key: {}, index: {}", key, index, e);
            return null;
        }
    }

    /**
     * 说明：将元素放到集合的末尾(右侧)(尾部)(最后)
     * description: 将list类型数据放入缓存(单个元素)
     * @author 朱正磊
     * @date 2019-10-18 13:40:18
     * @param key 键
     * @param value 值
     * @return 放入成功返回true，否则返回false
     */
    public <T> Boolean lrightPush(String key, T value) {
        try {
            zdyRedisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            log.error("将list类型数据放入缓存(单个元素)(右侧)异常信息：", e);
        }
        return false;
    }

    /**
     * 说明：将元素放到集合的末尾(右侧)(尾部)(最后)
     * description: 将list类型数据放入缓存并设置过期时间(单个元素)
     * @author 朱正磊
     * @date 2019-10-18 13:54:35
     * @param key 键
     * @param value 值
     * @param time 过期时间(秒)
     * @return 放入并设置过期时间成功返回true，否则返回false
     */
    public <T> Boolean lrightPush(String key, T value, Long time) {
        try {
            zdyRedisTemplate.opsForList().rightPush(key, value);
            return expire(key, time);
        } catch (Exception e) {
            log.error("将list类型数据放入缓存并设置过期时间(单个元素)(右侧)异常信息：", e);
        }
        return false;
    }

    /**
     * 说明：将元素放到集合的末尾(右侧)(尾部)(最后)
     * description: 将list类型数据放入缓存(多个元素)
     * @author 朱正磊
     * @date 2019-10-18 13:59:27
     * @param key 键
     * @param value 值
     * @return 放入成功返回true，否则返回false
     */
    public <T> Boolean lrightPushAll(String key, List<T> value) {
        try {
            zdyRedisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            log.error("将list类型数据放入缓存(多个元素)(右侧)异常信息：", e);
        }
        return false;
    }

    /**
     * 说明：将元素放到集合的末尾(右侧)(尾部)(最后)
     * description: 将list类型数据放入缓存并设置过期时间(多个元素)
     * @author 朱正磊
     * @date 2019-10-18 14:01:08
     * @param key 键
     * @param value 值
     * @param time 过期时间(秒)
     * @return 放入并设置过期时间成功返回true，否则返回false
     */
    public <T> Boolean lrightPushAll(String key, List<T> value, Long time) {
        try {
            zdyRedisTemplate.opsForList().rightPushAll(key, value);
            return expire(key, time);
        } catch (Exception e) {
            log.error("将list类型数据放入缓存并设置过期时间(多个元素)(右侧)异常信息：", e);
        }
        return false;
    }

    /**
     * 说明：将元素放到集合的开头(左侧)(头部)(首位)
     * description: 将list类型数据放入缓存(单个元素)
     * @author 朱正磊
     * @date 2025-03-12 16:40:18
     * @param key 键
     * @param value 值
     * @return 放入成功返回true，否则返回false
     */
    public <T> Boolean lleftPush(String key, T value) {
        try {
            zdyRedisTemplate.opsForList().leftPush(key, value);
            return true;
        } catch (Exception e) {
            log.error("将list类型数据放入缓存(单个元素)(左侧)异常信息：", e);
        }
        return false;
    }

    /**
     * 说明：将元素放到集合的开头(左侧)(头部)(首位)
     * description: 将list类型数据放入缓存并设置过期时间(单个元素)
     * @author 朱正磊
     * @date 2025-03-12 16:54:35
     * @param key 键
     * @param value 值
     * @param time 过期时间(秒)
     * @return 放入并设置过期时间成功返回true，否则返回false
     */
    public <T> Boolean lleftPush(String key, T value, Long time) {
        try {
            zdyRedisTemplate.opsForList().leftPush(key, value);
            return expire(key, time);
        } catch (Exception e) {
            log.error("将list类型数据放入缓存并设置过期时间(单个元素)(左侧)异常信息：", e);
        }
        return false;
    }

    /**
     * 说明：将元素放到集合的开头(左侧)(头部)(首位)
     * description: 将list类型数据放入缓存(多个元素)
     * @author 朱正磊
     * @date 2025-03-12 16:59:27
     * @param key 键
     * @param value 值
     * @return 放入成功返回true，否则返回false
     */
    public <T> Boolean lleftPushAll(String key, List<T> value) {
        try {
            zdyRedisTemplate.opsForList().leftPushAll(key, value);
            return true;
        } catch (Exception e) {
            log.error("将list类型数据放入缓存(多个元素)(左侧)异常信息：", e);
        }
        return false;
    }

    /**
     * 说明：将元素放到集合的开头(左侧)(头部)(首位)
     * description: 将list类型数据放入缓存并设置过期时间(多个元素)
     * @author 朱正磊
     * @date 2025-03-12 16:01:08
     * @param key 键
     * @param value 值
     * @param time 过期时间(秒)
     * @return 放入并设置过期时间成功返回true，否则返回false
     */
    public <T> Boolean lleftPushAll(String key, List<T> value, Long time) {
        try {
            zdyRedisTemplate.opsForList().leftPushAll(key, value);
            return expire(key, time);
        } catch (Exception e) {
            log.error("将list类型数据放入缓存并设置过期时间(多个元素)(左侧)异常信息：", e);
        }
        return false;
    }

    /**
     * description: 移除并返回列表的最后一个元素，从List右侧弹出元素(等价于Redis的RPOP命令)
     * @author 朱正磊
     * @date 2025-03-12 16:51:50
     * @param key 键，不能为null
     * @param elementType 弹出元素的类型Class对象
     * @return 返回列表的最后一个元素
     */
    public <R> R lrightPop(String key, Class<R> elementType) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(elementType, "元素类型Class不能为null！");

        try {
            Object value = zdyRedisTemplate.opsForList().rightPop(key);
            if (value == null) {
                return null;
            }

            if (elementType.isInstance(value)) {
                return elementType.cast(value);
            }

            throw new IllegalArgumentException(
                    String.format("List键[%s]弹出的元素[%s]类型[%s]与目标类型[%s]不匹配",
                            key, value, value.getClass().getName(), elementType.getName())
            );
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (Exception e) {
            log.error("从List右侧弹出元素异常，key: {}", key, e);
            return null;
        }
    }

    /**
     * 说明：这个方法的第二个参数是一个时间参数，表示客户端愿意等待的最长时间(以秒为单位)，
     *      如果在这个时间内列表中有元素被弹出，则返回该元素；如果没有元素被弹出，且等待时间超过指定的秒数，则返回null。
     * description: 移除并返回列表的最后一个元素(带超时等待)
     * @author 朱正磊
     * @date 2025-03-12 16:51:59
     * @param key 键，不能为null
     * @param time 等待时间(秒)，必须大于等于0
     * @param elementType 弹出元素的类型Class对象
     * @return 返回列表的最后一个元素，超时或空列表时返回null
     */
    public <R> R lrightPop(String key, Long time, Class<R> elementType) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(time, "等待时间不能为null！");
        Assert.isTrue(time >= 0, "等待时间不能为负数！");
        Assert.notNull(elementType, "元素类型Class不能为null！");

        try {
            Object value = zdyRedisTemplate.opsForList().rightPop(key, time, TimeUnit.SECONDS);
            if (value == null) {
                return null;
            }

            if (elementType.isInstance(value)) {
                return elementType.cast(value);
            }

            throw new IllegalArgumentException(
                    String.format("List键[%s]弹出的元素[%s]类型[%s]与目标类型[%s]不匹配",
                            key, value, value.getClass().getName(), elementType.getName())
            );
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (Exception e) {
            log.error("带超时从List右侧弹出元素异常，key: {}, 等待时间: {}秒", key, time, e);
            return null;
        }
    }

    /**
     * description: 移除并返回列表的第一个元素(对应Redis的LPOP命令)
     * @author 朱正磊
     * @date 2025-03-12 16:52:05
     * @param key 键，不能为null
     * @param elementType 弹出元素的类型Class对象
     * @return 返回列表的第一个元素，若列表为空则返回null
     */
    public <R> R lleftPop(String key, Class<R> elementType) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(elementType, "元素类型Class不能为null！");

        try {
            Object value = zdyRedisTemplate.opsForList().leftPop(key);
            if (value == null) {
                return null;
            }

            if (elementType.isInstance(value)) {
                return elementType.cast(value);
            }

            throw new IllegalArgumentException(
                    String.format("List键[%s]弹出的元素[%s]类型[%s]与目标类型[%s]不匹配",
                            key, value, value.getClass().getName(), elementType.getName())
            );
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (Exception e) {
            log.error("从List左侧弹出元素异常，key: {}", key, e);
            return null;
        }
    }

    /**
     * 说明：这个方法的第二个参数是一个时间参数，表示客户端愿意等待的最长时间(以秒为单位)，
     *      如果在这个时间内列表中有元素被弹出，则返回该元素；如果没有元素被弹出，且等待时间超过指定的秒数，则返回null。
     * description: 移除并返回列表的第一个元素(带超时等待)
     * @author 朱正磊
     * @date 2025-03-12 16:52:14
     * @param key 键，不能为null
     * @param time 等待时间(秒)，必须大于等于0
     * @param elementType 弹出元素的类型Class对象
     * @return 返回列表的第一个元素，超时或空列表时返回null
     */
    public <R> R lleftPop(String key, Long time, Class<R> elementType) {
        Assert.notNull(key, "键不能为null！");
        Assert.notNull(time, "等待时间不能为null！");
        Assert.isTrue(time >= 0, "等待时间不能为负数！");
        Assert.notNull(elementType, "元素类型Class不能为null！");

        try {
            Object value = zdyRedisTemplate.opsForList().leftPop(key, time, TimeUnit.SECONDS);
            if (value == null) {
                return null;
            }

            if (elementType.isInstance(value)) {
                return elementType.cast(value);
            }

            throw new IllegalArgumentException(
                    String.format("List键[%s]弹出的元素[%s]类型[%s]与目标类型[%s]不匹配",
                            key, value, value.getClass().getName(), elementType.getName())
            );
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (Exception e) {
            log.error("带超时从List左侧弹出元素异常，key: {}, 等待时间: {}秒", key, time, e);
            return null;
        }
    }

    /**
     * description: 根据索引更新list中的某条数据
     * @author 朱正磊
     * @date 2019-10-18 14:02:31
     * @param key 键
     * @param index 索引
     * @param value 值
     * @return 更新成功返回true，否则返回false
     */
    public <T> Boolean lupdateIndex(String key, Long index, T value) {
        try {
            zdyRedisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            log.error("根据索引更新list中的某条数据异常信息：", e);
        }
        return false;
    }

    /**
     * description: 从list集合中移除value值
     * @author 朱正磊
     * @date 2019-10-18 14:04:35
     * @param key 键
     * @param count 大于0按照从左到右的顺序移除count个值为value的数据；
     *              小于0按照从右到左的顺序移除-count个值为value的数据；
     *              等于0删除等于value的所有元素
     * @param value 值
     * @return 返回移除的个数
     */
    public <T> Long lremove(String key, Long count, T value) {
        try {
            return zdyRedisTemplate.opsForList().remove(key, count, value);
        } catch (Exception e) {
            log.error("从list集合中移除value值异常信息：", e);
        }
        return 0L;
    }

}
